/*-
 * Copyright (c) 2009 Jonathan Anderson
 * All rights reserved.
 *
 * WARNING: THIS IS EXPERIMENTAL SECURITY SOFTWARE THAT MUST NOT BE RELIED
 * ON IN PRODUCTION SYSTEMS.  IT WILL BREAK YOUR SOFTWARE IN NEW AND
 * UNEXPECTED WAYS.
 * 
 * This software was developed at the University of Cambridge Computer
 * Laboratory with support from a grant from Google, Inc. 
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/capability.h>

#include <errno.h>
#include <fcntl.h>

#include <iostream>
#include <sstream>

#include <QDir>
#include <QFileInfo>

#include "Cache.h"
#include "cap.h"
#include "dbus.h"
#include "ShowPowerbox.h"

using namespace std;


string ShowPowerbox::argString() const
{
	ostringstream oss;

	return oss.str();
}


int ShowPowerbox::readArguments(Client *client, struct ua_datum *d)
{
	if(ua_unmarshall_powerbox(d, &options) < 0) return -1;
}


int ShowPowerbox::run(Client *client)
{
	QList<File> files = display(client, &options);
	if(files.size() == 0) return 1;

	int len = files.size();

	struct ua_datum *fdcount = ua_marshall_int(len);
	if(ua_send(client->socket(), fdcount, NULL, 0) < 0) return -1;

	if(len == 0) cout << "\t\t<cancelled by user>";
	for(int i = 0; i < len; i++)
	{
		const File& file = files.at(i);
		string cap = file.capstring().toStdString();

		struct ua_datum *d = ua_marshall_string(cap.c_str(), cap.length());
		cout << "\t\t\"" << file.filename().toStdString() << '"' << endl;

		int fd = file.fd();
		if(ua_send(client->socket(), d, &fd, 1) < 0) return -1;
	}


	return 0;
}


QList<File> ShowPowerbox::display(Client *client,
                                  struct ua_powerbox_options *options)
{
	const int READ_FLAGS =
		(O_RDONLY | O_EXEC | O_NONBLOCK | O_SHLOCK | O_EXLOCK
		 | O_DIRECT | O_NOFOLLOW | O_NOCTTY | O_TTY_INIT);

	const cap_rights_t READ_CAPS = (CAP_READ | CAP_SEEK | CAP_FSTAT);

	int WRITE_FLAGS =
		(O_WRONLY | O_NONBLOCK | O_APPEND | O_CREAT | O_TRUNC | O_EXCL
		 | O_SHLOCK | O_EXLOCK | O_DIRECT | O_FSYNC | O_SYNC | O_NOFOLLOW
		 | O_NOCTTY | O_TTY_INIT);

	const cap_rights_t WRITE_CAPS =
		(CAP_WRITE | CAP_SEEK | CAP_FSTAT | CAP_FSYNC | CAP_FTRUNCATE);

	options->start_path = client->cwd().c_str();

	string client_name = client->name();
	options->window_title = (char*) malloc(client_name.length() + 64);

	const char *op;
	switch(options->operation)
	{
		case UA_OPEN_FILE:
			op = options->mult ? "Open Files" : "Open File";
			options->flags &= READ_FLAGS;
			options->rights &= READ_CAPS;
			break;

		case UA_SAVE_FILE:
			op = "Save File";
			options->flags &= WRITE_FLAGS;
			options->rights &= WRITE_CAPS;
			break;

		case UA_SELECT_DIR:
			op = "Choose Directory";
			options->flags = 0;
			options->rights = 0;
			break;
	};

	sprintf(options->window_title, "%s - %s", client_name.c_str(), op);


	// get the filenames from the user
	QStringList tmp;
	switch(options->ui)
	{
		case UA_DUMMY:
		case UA_QT:
		case UA_KDE:
			tmp = dbus_powerbox(options);
			break;

		default:
			fprintf(stderr, "Unhandled powerbox UI type %i\n",
			                options->ui);
	}
	const QStringList filenames = tmp;

	free(options->window_title);


	if(filenames.size() == 0)
	{
		fprintf(stderr, "Error opening DBus powerbox\n");
		return QList<File>();
	}
	else if((filenames.size() == 1) && (filenames.at(0).length() == 0))
	{
		errno = ECANCELED;
		return QList<File>();
	}



	// open the files using the requested flags and rights
	QList<File> files;
	int len = filenames.size();

	for(int i = 0; i < len; i++)
	{
		QString name(filenames.at(i));
		QFileInfo fullPath(name);

		// update the client's working directory
		if(i == 0) client->setCWD(fullPath.dir().path().toStdString());

		int fd = cap_open(name.toStdString().c_str(),
		                  options->flags, options->rights, options->umask);

		if(fd < 0) return QList<File>();

		File file(name, fd, options->rights);

		client->addFile(file);
		files << file;
	}

	return files;
}

